﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TurtleLib.Simulation;
using System.ComponentModel;

namespace TurtleLib.ComputerCraft
{
  public partial class Term
  {
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    internal event Action<Term, TermString[]> TextWritten;

    private int _x;
    private int _y;
    private int _width;
    private int _height;
    private TermString[] _data;
    private Term _outputTerminal;

    internal Term(int w, int h)
    {
      _outputTerminal = this;

      _x = 0;
      _y = 0;
      _width = w;
      _height = h;
      AllocTextContainers();

      TextWritten += delegate { };
    }

    protected void SetSize(int w, int h)
    {
      if (_outputTerminal != this)
      {
        _outputTerminal.SetSize(w, h);
        return;
      }

      _width = w;
      _height = h;
      AllocTextContainers();
    }

    private void AllocTextContainers()
    {
      _data = new TermString[_height];
      for (int i = 0; i < _data.Length; i++)
      {
        _data[i] = new TermString(GetBackgroundColor(), GetTextColor(), string.Empty.PadRight(_width, ' '), _width);
      }
    }

    /// <summary>
    /// Writes text to the screen
    /// </summary>
    /// <param name="text"></param>
    public virtual void write(string text)
    {
      if (_outputTerminal != this)
      {
        _outputTerminal.write(text);
        return;
      }

      if (_x >= _width || _y < 0 || _y >= _height || text.Length == 0)
        return;

      if (string.IsNullOrEmpty(text))
        return;

      TermString src = this[_y];
      TermString insert = new TermString(GetBackgroundColor(), GetTextColor(), text, text.Length);
      TermString final = src.Replace(insert, _x);

      this[_y] = final;
      _x += text.Length;

      TextWritten(this, _data);
    }

    /// <summary>
    /// Clears the entire screen
    /// </summary>
    public virtual void clear()
    {
      if (_outputTerminal != this)
      {
        _outputTerminal.clear();
        return;
      }

      AllocTextContainers();

      TextWritten(this, _data);
    }

    /// <summary>
    /// Clears te line the cursor is on
    /// </summary>
    public virtual void clearLine()
    {
      if (_outputTerminal != this)
      {
        _outputTerminal.clearLine();
        return;
      }

      if (_y < 0 || _y >= _height)
        return;

      this[_y] = new TermString(_width);
      TextWritten(this, _data);
    }

    /// <summary>
    /// The current cursor position
    /// </summary>
    /// <returns>tuple of x and y coordinates</returns>
    public virtual Tuple<int, int> getCursorPos()
    {
      if (_outputTerminal != this)
      {
        return _outputTerminal.getCursorPos();
      }

      return Tuple.Create(_x + 1, _y + 1);
    }

    /// <summary>
    /// Set the cursor position
    /// </summary>
    /// <param name="x">range from 1 to width</param>
    /// <param name="y">range from 1 to height</param>
    public virtual void setCursorPos(int x, int y)
    {
      if (_outputTerminal != this)
      {
        _outputTerminal.setCursorPos(x, y);
        return;
      }

      _x = x - 1;
      _y = y - 1;
    }

    /// <summary>
    /// Turn on/off the cursor blink
    /// </summary>
    /// <param name="enabled"></param>
    public virtual void setCursorBlink(bool enabled)
    {
      if (_outputTerminal != this)
      {
        _outputTerminal.setCursorBlink(enabled);
        return;
      }

      throw new NotImplementedException();
    }

    /// <summary>
    /// Check whether the terminal supports color (i.e. Advanced Terminal)
    /// </summary>
    /// <returns>true if the terminal supports color</returns>
    public virtual bool isColor()
    {
      if (_outputTerminal != this)
      {
        return _outputTerminal.isColor();
      }

      return false;
    }

    internal virtual colors GetBackgroundColor()
    {
      if (_outputTerminal != this)
      {
        return _outputTerminal.GetBackgroundColor();
      }

      return colors.black;
    }

    internal virtual colors GetTextColor()
    {
      if (_outputTerminal != this)
      {
        return _outputTerminal.GetTextColor();
      }

      return colors.white;
    }

    /// <summary>
    /// Size of the terminal
    /// </summary>
    /// <returns>Tuple of width and height</returns>
    public Tuple<int, int> getSize()
    {
      if (_outputTerminal != this)
      {
        return _outputTerminal.getSize();
      }

      return Tuple.Create(_width, _height);
    }

    internal TermString this[int y]
    {
      get
      {
        if (_outputTerminal != this)
        {
          return _outputTerminal[y];
        }

        if (y < 0 || y >= _height)
          return new TermString(_width);

        return _data[y] ?? new TermString(_width);
      }
      set
      {
        if (_outputTerminal != this)
        {
          _outputTerminal[y] = value;
          return;
        }

        if (y < 0 || y >= _height)
          return;

        _data[y] = value;
      }
    }

    /// <summary>
    /// Scroll the terminal cursor n lines (pushes existing lines up by n lines)
    /// </summary>
    /// <param name="n">number of lines to scroll</param>
    public virtual void scroll(int n)
    {
      if (_outputTerminal != this)
      {
        _outputTerminal.scroll(n);
        return;
      }

      if (Math.Abs(n) >= _height)
      {
        clear();
        return;
      }

      int start = n < 0 ? _height - 1 : 0;
      int dir = n < 0 ? -1 : 1;

      for (int y = 0; y < _height; y++)
      {
        int i = start + (y * dir);
        this[i] = this[i + n];
      }

      TextWritten(this, _data);
    }

    /// <summary>
    /// Redirect terminal output to a monitor or other redirect target. (Use peripheral.wrap to acquire a monitor "object".)
    /// </summary>
    /// <param name="target">Term object to receive output</param>
    public virtual void redirect(Term target)
    {
      _outputTerminal = target;
    }

    /// <summary>
    /// Restores terminal output to the previous target
    /// </summary>
    public virtual void restore()
    {
      _outputTerminal = this;
    }

    /// <summary>
    /// Set the text color (only supported in advanced monitors)
    /// </summary>
    /// <param name="color"></param>
    public virtual void setTextColor(colors color)
    {
      if (_outputTerminal != this)
      {
        _outputTerminal.setTextColor(color);
        return;
      }

      throw new NotSupportedException("Color not supported, use an advanced monitor instead");
    }

    /// <summary>
    /// Set the background color on the display. (only supported in advanced monitor
    /// </summary>
    /// <param name="color"></param>
    public virtual void setBackgroundColor(colors color)
    {
      if (_outputTerminal != this)
      {
        _outputTerminal.setBackgroundColor(color);
        return;
      }

      throw new NotSupportedException("Color not supported, use an advanced monitor instead");
    }
  }
}
